// Map's JavaScript Document

// Define map
var mapCenter = new google.maps.LatLng(32.0624, 35.3057);//32.800,35.017);
var mapZoom = 8;//12;
var mapOptions = {streetViewControl: false,
				  scaleControl: true,
				  mapTypeId : google.maps.MapTypeId.TERRAIN};//HYBRID};
var map;
var kmlOptions = {clickable: true,
                  preserveViewport: true,
                  suppressInfoWindows: true};
var teamskml = null;
var taskskml = null;
var matcheskml = null;
var geodesicsArray = [];
var markersArray = [];
var coloursArray = [];
var nextID = 0;

/**
 * loads the map container's CSS
 */
function LoadMapPage()
{
	// Set table CSS
	var height = window.innerHeight;
	$("#map_container").css({"width" : "95%", "height" : "95%"});
	$("#map_row").css({"height" : (height * 0.8) + "px"});
	$("#post_inputs_row").css({"height" : "30px"});
	$("#map_header_row th").css({"text-decoration" : "underline", "font-weight" : "bold", "height" : "40px"});
	$("#map_canvas").css({"width" : "100%", "height" : "100%", "padding" : "10px", "border" : "5px inset #FFFFFF"});
	$("#post_time_limit_minutes_input").change(function(event)
	{
		if (!((event.target.value <= 59) && (event.target.value >= 0)))
		{
			event.target.value = 0;
		}
	});
	$("#post_time_limit_hours_input").change(function(event)
	{
		if (!((event.target.value <= 23) && (event.target.value >= 0)))
		{
			event.target.value = 0;
		}
	});
	$("#post_time_limit_days_input, #post_capacity_input, #post_users_limit_input").change(function(event)
	{
		if (!(event.target.value >= 0))
		{
			event.target.value = 0;
		}
	});
	$("#post_type_input").change(function(event)
	{
		if (event.target.value == "team")
		{
			$("#post_users_limit_input").val("1");
		}
	});
	
	// Set buttons animation
	var buttons = ["#kml_teams_dl", "#kml_tasks_dl", "#kml_geodesics_dl",
	               "#kml_teams_show", "#kml_tasks_show", "#kml_geodesics_show"];
	
	for (elem in buttons)
	{
		$(buttons[elem])
			.mousedown(function(e) 
			{
				OnMouseDown(this.id);
			})
			.mouseup(function(e)
			{
				OnMouseUp(this.id);
			})
			.mouseout(function(e)
			{
				OnMouseOut(this.id);
			});
	}
	
	var nowString = (new Date()).toISOString();
	($("#post_time_input")[0]).value = nowString.substring(0, nowString.indexOf("."));
}

function RequireUpdate()
{
	$.post("../PostManager?action=requireupdate");
}

/**
 * takes LatLang and returns a marker object in the map
 */
function PlaceMarkerAt(location, inMap)
{
	var mapToPlace = ((inMap) ? map : null);
	var marker = new google.maps.Marker({position : location,
										draggable : true,
										map : mapToPlace});
	google.maps.event.addListener(marker, 'dragend', function(event)
	{
		marker.setPosition(event.latLng);
		RemoveEvent(marker.id);
		setTimeout(function() { SaveMarker(marker); }, 800);

	});
	return marker;
}

/**
 * removes given marker from the map
 */
function RemoveMarker(marker)
{
	marker.setMap(null);
}

function AddClickEvent(marker)
{
	google.maps.event.addListener(marker, 'click', function()
	{
		var geocoder = new google.maps.Geocoder();
	    geocoder.geocode({'latLng' : marker.getPosition()}, function(data, status)
	    {
	    	marker.address = "Unknown";

	    	if (status == google.maps.GeocoderStatus.OK)
	    	{
	    		marker.address = data[0].formatted_address;
	    	}

	    	var infoBubble = NewIWSInfoBubble(marker);
	    	infoBubble.open(map, marker);
	    });
	});
}

/**
 * Creates post marker based on given fields
 * Posts marker to map, attaches all fields to the marker and adds marker to array
 */
function CreatePostMarker(fields)
{
	var id = fields[0];
	var email = fields[1];
	var type = fields[2];
	var geoTag = new Array();
	var point = fields[3].split(" ");
	geoTag[0] = new Number(point[0]);
	geoTag[1] = new Number(point[1]);
	var location = new google.maps.LatLng(geoTag[0], geoTag[1]);
	var time = fields[4];
	var timeLimit = fields[5];
	var capacity = fields[6];
	var abilities = fields[7];
	var usersLimit = fields[8];
	var users = fields[9];
	var content = fields[10];

	var bounds = map.getBounds();
	var inMap = bounds.contains(location);
	
	var marker = PlaceMarkerAt(location, inMap);
	marker.id = new Number(id);
	marker.email = email;
	marker.type = type;
	if (marker.type == "1")
	{
		marker.setIcon("https://maps.google.com/mapfiles/kml/shapes/schools_maps.png");
	}
	marker.geoTag = location;
	marker.time = new Number(time);
	marker.timeLimit = new Number(timeLimit);
	marker.capacity = new Number(capacity);
	marker.abilities = abilities.split("@") || [];
	marker.usersLimit = new Number(usersLimit);
	marker.users = users.split("@") || [];
	marker.content = content;
	
	AddClickEvent(marker);
	
	markersArray[id] = marker;
}

function ClearMapArray(array)
{
	for (var i = 0; i < array.length; i++)
	{
		if (array[i] != null)
		{
			if (array[i].bubble != null)
			{
				array[i].bubble.close();
			}
			array[i].setMap(null);
			array[i] = null;
		}
	}
	array = [];
}

/**
 *  populate posts, gets posts as comma separated fields in EOF separated rows
 */
function PopulatePosts(posts)
{
	ClearMapArray(markersArray);
	// now we have an array of post rows
	var postsArray = posts.split("\n");
	
	for (var index = 0; index < postsArray.length; index++)
	{
		if (postsArray[index] == "")
		{
			continue;
		}
		// now we have an array of fields
		var fieldsArray = postsArray[index].split(",");
		if (fieldsArray.length != 11) continue;
		
		CreatePostMarker(fieldsArray);
	}
}

function GetTaskArray()
{
	taskArray = [];
	
	for (var i = 0; i < markersArray.length; i++)
	{
		if ((markersArray[i] != null) && (markersArray[i].type == "0"))
		{
			taskArray[taskArray.length] = markersArray[i];
		}
	}
	
	if (taskArray.length != 0)
	{
		var now = new Date().getTime();
		taskArray.sort(function(a, b)
		{
			var aStrength = ((a.time.valueOf() != a.timeLimit.valueOf())
								? (a.timeLimit.valueOf() - now) / 1000
									: (a.time.valueOf()));
			var bStrength = ((b.time.valueOf() != b.timeLimit.valueOf())
								? (b.timeLimit.valueOf() - now) / 1000
									: (b.time.valueOf()));
			return aStrength - bStrength;
		});
	}
	
	return taskArray;
}

function GetTeamArray()
{
	teamArray = [];
	
	for (var i = 0; i < markersArray.length; i++)
	{
		if ((markersArray[i] != null) && (markersArray[i].type == "1"))
		{
			teamArray[teamArray.length] = markersArray[i];
		}
	}
	
	return teamArray;
}

function FillColoursArray(num)
{
	if (coloursArray.length > num)
	{
		return;
	}
	$.get("http://www.random.org/integers/?num=999&min=0&max=15&col=6&base=16&format=plain&rnd=new",
	    function(data, status)
	    {
			if (status == "success")
			{
				var colours = data.trim().split("\n");
				for (var i = 0; i < colours.length; i++)
				{
					coloursArray.push("#" + colours[i].trim().replace(/\n/g, "").replace(/\t/g, ""));
				}
			}
	    });
}

function MatchOnMap(task, team)
{
	var geodesicOptions = {strokeColor : coloursArray.pop(),
	                       strokeOpacity: 1.0,
	                       strokeWeight: 2,
	                       geodesic: true};
	var geodesic = new google.maps.Polyline(geodesicOptions);
	geodesic.setMap(map);
	geodesic.getPath().push(team.getPosition());
	geodesic.getPath().push(task.getPosition());
	geodesicsArray.push(geodesic);
}

function FilterForAbilities(task, teamArray)
{
	newTeamArray = [];
	
	// sort for abilities length
	teamArray.sort(function(a, b)
	{
		return a.abilities.length - b.abilities.length;
	});
	
	for (var j = 0; j < teamArray.length; j++)
	{
		teamAbilities = teamArray[j].abilities;
		
		// eliminate the obvious
		if (teamAbilities.length < task.abilities.length)
		{
			continue;
		}
		
		// eliminate the incompatible
		for (var k = 0; k < task.abilities.length; k++)
		{
			// that is, a task abilitiy that a team doesn't have
			if (teamAbilities.indexOf(task.abilities[k]) == -1)
			{
				continue;
			}
		}
		newTeamArray.push(teamArray[j]);
	}
	
	return newTeamArray;
}

function FilterForCapacity(task, teamArray)
{
	newTeamArray = [];
	
	// sort for capacity
	teamArray.sort(function(a, b)
	{
		return a.capacity.valueOf() - a.capacity.valueOf();
	});
	
	// eliminate the obvious
	for (var j = 0; j < teamArray.length; j++)
	{
		if (teamArray[j].capacity >= task.capacity)
		{
			newTeamArray.push(teamArray[j]);
		}
	}
	return newTeamArray;
}

function CheckForUpdates()
{
	$.get("../PostManager?action=needupdate", function(data, status)
	{
		if (status == "success")
		{
			ClearMapArray(geodesicsArray);
			// get all posts from database and populate them
			// get all posts from database and populate them
			$.get("../PostManager?action=getallposts", function(data, status)
			{
				if ((status = "success") && (data != "error"))
				{
					PopulatePosts(data);
					var geodesicsForm = [];

					// get task array - sorted by time remaining and then by time
					var taskArray = GetTaskArray();
					// get team array
					var teamArray = GetTeamArray();
					FillColoursArray(taskArray.length);
					if ((taskArray.length != 0) && (teamArray.length != 0))
					{
						// for each task,
						for (var i = 0; i < taskArray.length; i++)
						{
							var task = taskArray[i];
							
							newTeamArray = FilterForAbilities(task, teamArray);
							if (newTeamArray.length == 0)
							{
								continue;
							}
							
							newTeamArray = FilterForCapacity(task, newTeamArray);
							if (newTeamArray.length == 0)
							{
								continue;
							}
							
							// here, every team in the array should have the minimum necessary
							// abilities and capacity
							
							// sort team array by abilities, than capacity, than distance from task
							newTeamArray.sort(function(a, b)
							{
								var aExactAbilities = ((a.abilities.length == task.abilities.length) ? 1 : 0);
								var bExactAbilities = ((b.abilities.length == task.abilities.length) ? 1 : 0);
								var aExactCapacity = ((a.capacity == task.capacity) ? 1 : 0);
								var bExactCapacity = ((b.capacity == task.capacity) ? 1 : 0);
								var aDistance = google.maps.geometry.spherical.computeDistanceBetween(a.getPosition(), taskArray[i].getPosition());
								var bDistance = google.maps.geometry.spherical.computeDistanceBetween(b.getPosition(), taskArray[i].getPosition());
								var aStrength = aExactAbilities * (-100000) + a.abilities.length * 10000 + 
												aExactCapacity * (-10000) + a.capacity * 1000 +
												aDistance / 1000;
								var bStrength = bExactAbilities * (-100000) + b.abilities.length * 10000 +
												bExactCapacity * (-10000) + b.capacity * 1000 +
												bDistance / 1000;
								return aStrength - bStrength;
							});
							
							MatchOnMap(taskArray[i], newTeamArray[0]);
							geodesicsForm.push(taskArray[i].id.toString() + "," +
							                   taskArray[i].getPosition().lat() + "%20" + taskArray[i].getPosition().lng() + "," +
							                   newTeamArray[0].id.toString() + "," +
							                   newTeamArray[0].getPosition().lat() + "%20" + newTeamArray[0].getPosition().lng());
							teamArray.splice(teamArray.indexOf(newTeamArray[0]), 1);
						}
					}
					$.post("../PostManager?action=updategeodesics&geodesics=" + geodesicsForm.join("@"));
					$.post("../XMLDump&refresh=true");
				}
			});
		}
	});
}

/**
 * this function is called when the Maps library finish loading
 * It sets the map canvas, defines the map click event and get the number of posts 
 */
function MapInitialise()
{
	// Populate map
	map = new google.maps.Map($("#map_canvas")[0], mapOptions);
	map.setCenter(mapCenter);
	map.setZoom(mapZoom);
	
	// define map click
	google.maps.event.addListener(map, 'click', function(event) 
	{
		MapClick(event);
	});
	google.maps.event.addListener(map, 'dragend', function(event)
	{
		if (AllKMLOff())
		{
			// get all posts from database and populate them
			$.get("../PostManager?action=getallposts", function(data, status)
			{
				if ((status = "success") && (data != "error"))
				{
					PopulatePosts(data);
				}
			});
		}
		mapCenter = map.getCenter();
	});
	google.maps.event.addListener(map, 'zoom_changed', function(event)
	{
		mapZoom = map.getZoom();
	});
	
	$.get("../PostManager?action=getnextid", function(data, status)
    {
		if (status == "success")
		{
			nextID = new Number(data);

			// comment this out to disable auto matching.
			setInterval(function() { CheckForUpdates(); },5000);

			RequireUpdate();
		}
    });
}

function ManualTimeConvert()
{
	var timeString = $("#post_time_input").val();
	var year = timeString.substr(0, 4);
	var month = timeString.substr(5, 2);
	var day = timeString.substr(8, 2);
	var hours = timeString.substr(11, 2);
	var minutes = timeString.substr(14, 2);
	var seconds = timeString.substr(17, 2);
	return new Date(year, month, day, hours, minutes, seconds, 0).getTime();
}

function SaveMarker(marker)
{
	var form = "?action=savepost" +
		"&id=" + marker.id +
		"&email=" + marker.email +
		"&type=" + marker.type +
		"&geotag=" + marker.getPosition().lat() + " " + marker.getPosition().lng() +
		"&time=" + marker.time +
		"&timelimit=" + marker.timeLimit +
		"&capacity=" + marker.capacity +
		"&abilities=" + (marker.abilities.join()).replace(/,/g, "@") +
		"&userslimit=" + marker.usersLimit +
		"&users=" + (marker.users.join()).replace(/,/g, "@") +
		"&content=" + marker.content;

	$.post("../UserManager" + form, function(data, status)
	{
		if ((status == "success") && (data != "error"))
		{
			AddClickEvent(marker);
			
			markersArray[nextID] = marker;

			RequireUpdate();
		}
		else
		{
			marker.setMap(null);
		}
	});
}

/**
 * This function defines what to do when the map is clicked
 */
function MapClick(event)
{
	$.get("../PostManager?action=getnextid", function(data, status)
    {
  		if (status == "success")
  		{
  			nextID = new Number(data);

  			var marker = PlaceMarkerAt(event.latLng, true);
	  		marker.id = new Number(nextID);
	  		marker.email = sessionStorage.user;
	  		marker.type = (($("#post_type_input").val().toLowerCase() == "task") ? ("0") : ("1"));
	  		if (marker.type == "1")
	  		{
	  			marker.setIcon("https://maps.google.com/mapfiles/kml/shapes/schools_maps.png");
	  		}
	  		// saving timestamps as milisecond notation
	  		marker.time = new Number($("#post_time_input")[0].valueAsNumber);
	  		if (isNaN(marker.time))
	  		{
	  			marker.time = ManualTimeConvert();		
	  		}
	  		var days = new Number($("#post_time_limit_days_input").val());
	  		var hours = days * 24 + new Number($("#post_time_limit_hours_input").val());
	  		var minutes = hours * 60 + new Number($("#post_time_limit_minutes_input").val());
	  		marker.timeLimit = new Number(marker.time + minutes * 60 * 1000);
	  		marker.capacity = new Number($("#post_capacity_input").val());
	  		marker.abilities = $("#post_abilities_input").val() || [];
	  		marker.usersLimit = new Number($("#post_users_limit_input").val());
	  		marker.users = [];
	  		marker.users[0] = marker.email;
	  		marker.content = $("#post_content_input").val();
	  		
	  		SaveMarker(marker);
		}
    });
}

/**
 *  creates standard InfoBubble
 */
function NewEmptyInfoBubble()
{
	return new InfoBubble(
	{
		arrowPosition : "50%",
		arrowSize : "15",
		arrowStyle : "0",
		borderColor : "#bbb",
		borderRadius : "5",
		borderWidth : "2",
		maxHeight : "200",
		maxWidth : "300",
		map : map,
		minHeight : "50",
		minWidth : "100",
		padding : "10",
	});
}

function TimeLimitString(segments)
{
	if (segments.length == 0)
	{
		return "None.";
	}
	return "" + segments[0] + " days, " + segments[1] + " hours, " + segments[2] + " minutes.";
}

function TimeLimitSegments(time, timeLimit)
{
	var diff = timeLimit - time;
	var segments = [];
	
	if (diff != 0)
	{
		diff /= 1000 * 60;
		var minutes = Math.floor(diff % 60);
		diff /= 60;
		var hours = Math.floor(diff % 24);
		var days = Math.floor(diff / 24);

		segments[0] = days;
		segments[1] = hours;
		segments[2] = minutes;
	}
	
	return segments;
}

function PrintArray(abilities)
{
	if (abilities.length == 0)
	{
		return "None.";
	}
	
	return abilities.join().replace(/,/g, ", ");
}

function RemoveEvent(id)
{
	var form = "?action=removepost&email=" + sessionStorage.user + "&id=" + id.toString();

	$.post("../UserManager" + form, function(data, status)
	{
		if (status == "success")
		{
			var marker = markersArray[id];
			if (marker.bubble != null)
			{
				marker.bubble.close();
			}
			RemoveMarker(marker);
			markersArray[id] = null;
			RequireUpdate();
		}
	});
}

function JoinTeam(id)
{
	var email = sessionStorage.user;
	var form = "?action=jointeam&id=" + id.toString() + "&email=" + email;
	$.post("../PostManager" + form, function(data, status)
	{
		if (status == "success")
		{
			var marker = markersArray[id];
			marker.users[marker.users.length] = email;
			marker.bubble.close();
			marker.bubble = NewIWSInfoBubble(marker);
			marker.bubble.open(map, marker);
			RequireUpdate();
		}
	});
}

function LeaveTeam(id)
{
	var email = sessionStorage.user;
	var form = "?action=leaveteam&id=" + id.toString() + "&email=" + email;
	$.post("../PostManager" + form, function(data, status)
	{
		if (status == "success")
		{
			var marker = markersArray[id];
			var userId = marker.users.indexOf(email);
			marker.users.splice(userId, 1);
			marker.bubble.close();
			marker.bubble = NewIWSInfoBubble(marker);
			marker.bubble.open(map, marker);
			RequireUpdate();
		}
	});
}

function NewIWSInfoBubble(marker)
{
	var bubble = NewEmptyInfoBubble();
	var email = sessionStorage.user;
	var task = new Boolean(marker.type == "0");
	var contentSuffix = (task.valueOf()) ? "Required: " : "Available: ";
	var tabStartLeft = "<div style=\"text-align: left;\">";
	var tabStartRight = "<div style=\"text-align: right;\">";
	var tabEnd = "</div>";

	var basicInfoTab = tabStartLeft + "<p>Post ID: " + marker.id + "</p>"
					+ "<p>Creator E-Mail: " + marker.email + "</p>"
					+ "<p>Post Type: " + ((task.valueOf()) ? ("Task") : ("Team")) + "</p>"
					+ "<p>Time Created: " + new Date(marker.time).toString() + "</p>"
					+ "<p>Address: " + marker.address
					+ tabEnd;
	if (marker.email == email)
	{
		basicInfoTab +=	tabStartRight
					+ "<input type=\"button\" value=\"Remove Event\" onclick=\"RemoveEvent(" + marker.id + ")\" />"
					+ tabEnd;
	}
	var time = ((marker.time.valueOf() == marker.timeLimit.valueOf()) ? (marker.time) : (new Date().getTime()));
	var eventInfoTab = tabStartLeft + "<p>Time Limit: " + 
						TimeLimitString(TimeLimitSegments(time, marker.timeLimit)) + "</p>"
					+ "<p>Capacity " + contentSuffix + marker.capacity + "</p>"
					+ "<p>Abilities " + contentSuffix + PrintArray(marker.abilities) + "</p>" + tabEnd;
	var userInfoTab = tabStartLeft + "<p>Users Limit: " + marker.usersLimit + "</p>"
					+ "<p>Users: " + PrintArray(marker.users) + "</p>" + tabEnd;
	if ((marker.email != email) && (marker.users.length < marker.usersLimit))
	{
		var joinLeave = ((marker.users.indexOf(email) == -1) 
							? ("<input type=\"button\" value=\"Join Team\" onclick=\"JoinTeam(" + marker.id + ")\" />")
								: ("<input type=\"button\" value=\"Leave Team\" onclick=\"LeaveTeam(" + marker.id + ")\" />"));
		userInfoTab += tabStartRight
					+ joinLeave
					+ tabEnd;
	}
	var contentTab = tabStartLeft + "<p>" 
					+ ((marker.content.length == 0) ? "None." : marker.content)
					+ "</p>" + tabEnd;
	
	bubble.addTab("Basic Info", basicInfoTab);
	bubble.addTab("Event Info", eventInfoTab);
	if (!task.valueOf())
	{
		bubble.addTab("User Info", userInfoTab);
	}
	bubble.addTab("Extra Content", contentTab);
	
	marker.bubble = bubble;
	
	return bubble;
}

// KML Functions
function ToggleKMLDownloads()
{
	$(".kml_display_line").css("display", "none");
	if ($(".kml_download_line").css("display") == "none")
	{
		$(".kml_download_line").css("display", "table-cell");
	}
	else
	{
		$(".kml_download_line").css("display", "none");
	}
}

function ToggleKMLDisplays()
{
	$(".kml_download_line").css("display", "none");
	if ($(".kml_display_line").css("display") == "none")
	{
		$(".kml_display_line").css("display", "table-cell");
	}
	else
	{
		$(".kml_display_line").css("display", "none");
	}
}

function DownloadKML(file)
{
	window.open("../XMLDump?file=" + file + "&save=true","_blank");
}

function DownloadTeams()
{
	DownloadKML("teams");
}

function DownloadTasks()
{
	DownloadKML("tasks");
}

function DownloadMatches()
{
	DownloadKML("geodesics");
}

function ShowKML(data, kml)
{
	if ((kml == null) || (kml.getMap() == null))
	{
		kml = new google.maps.KmlLayer(kmlOptions);
		kml.setUrl(data);
		kml.setMap(map);
		ClearMapArray(markersArray);
		ClearMapArray(geodesicsArray);
	}
	else
	{
		kml.setMap(null);
	}
	return kml;
}

function ShowTeams()
{
	$.get("../XMLDump?file=teams&save=false", function(data, status)
	{
		if (status == "success")
	  	{
	  		teamskml = ShowKML(data, teamskml);
	  	}

		if (AllKMLOff())
		{
			RequireUpdate();
		}
	});
}

function ShowTasks()
{
	$.get("../XMLDump?file=tasks&save=false", function(data, status)
	{
		if (status == "success")
		{
			taskskml = ShowKML(data, taskskml);
		}
		
		if (AllKMLOff())
		{
			RequireUpdate();
		}
	});
}

function ShowMatches()
{
	$.get("../XMLDump?file=geodesics&save=false", function(data, status)
	{
		if (status == "success")
		{
			matcheskml = ShowKML(data, matcheskml);
		}

		if (AllKMLOff())
		{
			RequireUpdate();
		}
	});
}

function AllKMLOff()
{
	return (((teamskml == null) || (teamskml.getMap() == null)) && 
				((taskskml == null) || (taskskml.getMap() == null)) && 
				((matcheskml == null) || (matcheskml.getMap() == null)));
}
